package common

import (
	"bufio"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

// POM文件结构
type Pom struct {
	XMLName    xml.Name `xml:"project"`
	GroupID    string   `xml:"groupId"`
	ArtifactID string   `xml:"artifactId"`
	Version    string   `xml:"version"`
	Parent     struct {
		GroupID string `xml:"groupId"`
		Version string `xml:"version"`
	} `xml:"parent"`
}

// 依赖文件
type DependencyFiles struct {
	Dir     string
	Jars    []string
	Pom     string
	JavaDoc string
	Sources string
}

// 文件元数据
type ArtifactMetadata struct {
	GroupID    string
	ArtifactID string
	Version    string
	Classifier string
	Extension  string
}

// 上传任务结构体
type uploadTask struct {
	dfiles DependencyFiles // 文件路径
	pom    Pom             // 元数据
	config *NexusConfig    // Nexus配置
}

// 日志结构体
type logTask struct {
	message string // 消息
	name    string // 元数据
}

var (
	fileMutex  sync.Mutex // 保护文件访问的互斥锁
	logFile    *os.File
	recordFile *os.File
)

func RunMavenUpload(config *NexusConfig) {
	upType := "mvn"
	if config.UseMvn {
		upType = "mvn"
		fmt.Println("使用maven命令上传...")
	} else {
		upType = "put"
		fmt.Println("使用put请求直接上传...")
	}
	// 开始计时
	start := time.Now()
	var err error
	localRepo := config.MavenRepo // 本地Maven仓库路径
	var logMap map[string]bool = initLog()
	var (
		successCount uint64
		failCount    uint64
	)
	// 创建任务通道
	taskChan := make(chan uploadTask, 1000)
	logChan := make(chan logTask, 1000)

	var wg sync.WaitGroup
	var wgLog sync.WaitGroup
	//启动一个协程负载写入日志
	wgLog.Add(1)
	go writeToLog(logChan, &wgLog)
	// 启动并发上传goroutines
	for i := 0; i < config.Concurrency; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for task := range taskChan {
				if task.config.UseMvn {
					err = deployFile(task.dfiles, task.pom, task.config, logChan)
				} else {
					err = uploadFile(task.dfiles.Dir+"/"+task.dfiles.Pom, task.pom, task.config)
					if task.dfiles.Jars != nil && len(task.dfiles.Jars) > 0 {
						for _, jar := range task.dfiles.Jars {
							err = uploadFile(task.dfiles.Dir+"/"+jar, task.pom, task.config)
						}
					}
				}
				if err != nil {
					atomic.AddUint64(&failCount, 1)
					fmt.Printf("❌ %s上传失败: %s - %v\n", upType, task.dfiles.Pom, err)
				} else {
					atomic.AddUint64(&successCount, 1)
					fmt.Printf("✅ %s上传成功 线程：%d  任务: %d / %d: %s:%s:%s\n",
						upType,
						i,
						successCount,
						failCount+successCount,
						task.pom.GroupID,
						task.pom.ArtifactID,
						task.pom.Version)
				}
			}
		}()
	}

	// 遍历本地仓库，生成上传任务
	if err := filepath.WalkDir(localRepo, func(path string, d os.DirEntry, err error) error {
		if err != nil {
			return err
		}
		if d.IsDir() {
			return nil // 跳过目录
		}

		// 过滤需要上传的文件类型,目录下有POM文件才上传
		ext := filepath.Ext(path)
		if ext != ".pom" {
			return nil
		}

		// 解析POM文件获取基本坐标
		pomPath := findPomFile(path)
		if pomPath == "" {
			fmt.Printf("⚠️ 未找到POM文件: %s\n", path)
			return nil
		}

		coords, err := parsePomFile(pomPath)
		if err != nil {
			fmt.Printf("⚠️ 解析POM失败: %s - %v\n", pomPath, err)
			return nil
		}
		if logMap[buildDependencyName(coords)] {
			//fmt.Println("⚠️ 已经上传过: ", path)
			return nil
		}
		// 解析POM文件获取基本坐标
		pomFile := filepath.Base(pomPath)
		jarFile := findJarFiles(path)
		javaDocFile := findJavaDocFile(path)
		javaSourceFile := findSourcesFile(path)
		depFiles := DependencyFiles{
			Dir:     filepath.Dir(path),
			Pom:     pomFile,
			Jars:    jarFile,
			JavaDoc: javaDocFile,
			Sources: javaSourceFile,
		}
		// 添加到任务通道
		taskChan <- uploadTask{
			dfiles: depFiles,
			pom:    coords,
			config: config,
		}
		return nil
	}); err != nil {
		fmt.Printf("遍历仓库失败: %v\n", err)
		close(taskChan)
		return
	}

	// 关闭任务通道并等待所有任务完成
	close(taskChan)
	wg.Wait()
	fmt.Println("\n上传任务全部完成")
	close(logChan)
	wgLog.Wait()
	fmt.Println("日志写入完成")
	totalElapsed := time.Since(start)
	log.Printf("上传完成统计:\n\t\t总耗时: %s\n\t\t成功: %d 个\n\t\t失败: %d 个",
		totalElapsed, successCount, failCount,
	)
}

func initLog() map[string]bool {
	var err error
	logName := "maven_log.txt"
	recordName := ".maven.txt"
	logFile, err = os.Create(logName)
	if err != nil {
		log.Fatalf("创建日志文件失败: %v", err)
	}

	recordFile, err = os.OpenFile(recordName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		if !os.IsExist(err) {
			log.Fatalf("创建文件失败: %v", err)
		}
	}
	dataMap := make(map[string]bool) // 使用空结构体节省内存
	scanner := bufio.NewScanner(recordFile)
	for scanner.Scan() {
		line := scanner.Text()
		dataMap[line] = true // 存储行内容作为键
	}
	if err := scanner.Err(); err != nil {
		fmt.Printf("读取文件失败: %v\n", err)
		return dataMap
	}
	fmt.Printf("共读取 %d 行记录\n", len(dataMap))
	return dataMap
}

func writeToLog(logChan chan logTask, wgLog *sync.WaitGroup) {
	defer func() {
		logFile.Close()
		recordFile.Close()
		wgLog.Done()
	}()
	// 使用 for-range 持续读取通道，直到通道关闭
	for logTask := range logChan {
		fileMutex.Lock()
		_, err := logFile.WriteString(logTask.message + "\n")
		if err != nil {
			log.Printf("写入日志失败: %v", err)
		}
		if logTask.name != "" {
			_, err = recordFile.WriteString(logTask.name + "\n")
			if err != nil {
				log.Printf("写入日志失败1: %v", err)
			}
		}
		fileMutex.Unlock()
	}
}

// 查找POM文件
func findPomFile(filePath string) string {
	dir := filepath.Dir(filePath)
	pomPattern := filepath.Join(dir, "*.pom")
	pomFiles, err := filepath.Glob(pomPattern)
	if err != nil || len(pomFiles) == 0 {
		return ""
	}
	return pomFiles[0] // 返回第一个匹配的POM文件
}

// 查找普通JAR文件（排除-javadoc.jar和-sources.jar）
func findJarFiles(filePath string) []string {
	dir := filepath.Dir(filePath)
	// 修正模式匹配，使用*.jar而非.jar
	docPattern := filepath.Join(dir, "*.jar")
	docFiles, err := filepath.Glob(docPattern)
	if err != nil || len(docFiles) == 0 {
		return nil
	}
	// 遍历所有匹配的文件，筛选符合条件的JAR
	var result []string
	for _, file := range docFiles {
		fileName := filepath.Base(file)
		// 排除-javadoc.jar和-sources.jar后缀的文件
		if !strings.HasSuffix(fileName, "-javadoc.jar") &&
			!strings.HasSuffix(fileName, "-sources.jar") {
			result = append(result, fileName)
		}
	}
	return result
}

// 查找javaDoc文件
func findJavaDocFile(filePath string) string {
	dir := filepath.Dir(filePath)
	docPattern := filepath.Join(dir, "-javadoc.jar")
	docFiles, err := filepath.Glob(docPattern)
	if err != nil || len(docFiles) == 0 {
		return ""
	}
	return filepath.Base(docFiles[0]) // 返回第一个匹配的javaDoc文件
}

// 查找sources文件
func findSourcesFile(filePath string) string {
	dir := filepath.Dir(filePath)
	docPattern := filepath.Join(dir, "-sources.jar")
	docFiles, err := filepath.Glob(docPattern)
	if err != nil || len(docFiles) == 0 {
		return ""
	}
	return filepath.Base(docFiles[0]) // 返回第一个匹配的sources文件
}

// 解析POM文件获取坐标
func parsePomFile(pomPath string) (Pom, error) {
	data, err := os.ReadFile(pomPath)
	if err != nil {
		return Pom{}, fmt.Errorf("读取POM失败: %w", err)
	}

	// 转换为字符串
	str := string(data)
	// 替换关键字
	newStr := strings.ReplaceAll(str, "iso-8859-1", "utf-8")
	newStr = strings.ReplaceAll(newStr, "ISO-8859-1", "utf-8")
	// 转回字节切片
	newBytes := []byte(newStr)

	var pom Pom
	if err := xml.Unmarshal(newBytes, &pom); err != nil {
		return Pom{}, fmt.Errorf("解析POM失败: %w", err)
	}
	if pom.GroupID == "" {
		pom.GroupID = pom.Parent.GroupID
	}
	if pom.Version == "" {
		pom.Version = pom.Parent.Version
	}
	if strings.HasPrefix(pom.Version, "${") {
		baseName := filepath.Base(pomPath)
		version := strings.ReplaceAll(baseName, pom.ArtifactID+"-", "")
		version = strings.ReplaceAll(version, ".pom", "")
		pom.Version = version
	}
	if pom.GroupID == "" || pom.ArtifactID == "" || pom.Version == "" {
		return Pom{}, errors.New("POM缺少必要字段（groupId/artifactId/version）")
	}

	return pom, nil
}

// 从文件名提取classifier和extension
func extractMetadata(filePath string, pom Pom) (ArtifactMetadata, error) {
	baseName := filepath.Base(filePath)
	ext := filepath.Ext(baseName)
	fileName := strings.TrimSuffix(baseName, ext)
	// 构建预期的basePattern: artifactId-version
	basePattern := fmt.Sprintf("%s-%s", pom.ArtifactID, pom.Version)

	// 检查文件名是否匹配basePattern
	if !strings.HasPrefix(fileName, basePattern) {
		return ArtifactMetadata{}, fmt.Errorf("文件名格式不匹配: %s", baseName)
	}

	// 提取classifier (如果存在)
	classifier := ""
	remaining := strings.TrimPrefix(fileName, basePattern)
	if remaining != "" {
		// 移除开头的连字符
		if strings.HasPrefix(remaining, "-") {
			classifier = strings.TrimPrefix(remaining, "-")
		}
	}

	// 特殊处理SHA1文件
	if strings.HasSuffix(ext, ".sha1") {
		originalExt := strings.TrimSuffix(ext, ".sha1")
		return ArtifactMetadata{
			GroupID:    pom.GroupID,
			ArtifactID: pom.ArtifactID,
			Version:    pom.Version,
			Classifier: classifier,
			Extension:  originalExt + ".sha1",
		}, nil
	}

	return ArtifactMetadata{
		GroupID:    pom.GroupID,
		ArtifactID: pom.ArtifactID,
		Version:    pom.Version,
		Classifier: classifier,
		Extension:  ext,
	}, nil
}

// 上传文件到Nexus
func uploadFile(filePath string, pom Pom, config *NexusConfig) error {
	// 提取文件名中的classifier和extension
	metadata, err := extractMetadata(filePath, pom)
	if err != nil {
		fmt.Printf("⚠️ 提取元数据失败: %s - %v\n", filePath, err)
		return fmt.Errorf("⚠️ 提取元数据失败: %w", err)
	}
	// 构建上传路径
	groupPath := strings.ReplaceAll(metadata.GroupID, ".", "/")
	fileName := buildFileName(metadata)
	uploadPath := fmt.Sprintf("%s/%s/%s/%s", groupPath, metadata.ArtifactID, metadata.Version, fileName)
	uploadURL := config.MavenURL + "/" + uploadPath

	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return fmt.Errorf("打开文件失败: %w", err)
	}
	defer file.Close()

	// 创建请求
	req, err := http.NewRequest("PUT", uploadURL, file)
	if err != nil {
		return fmt.Errorf("创建请求失败: %w", err)
	}
	req.SetBasicAuth(config.Username, config.Password)
	req.Header.Set("User-Agent", "Maven-Deploy-Tool/1.0")

	// 设置Content-Type
	contentType := getContentType(fileName)
	if contentType != "" {
		req.Header.Set("Content-Type", contentType)
	}

	// 设置超时
	client := &http.Client{
		Timeout: time.Duration(config.Timeout) * time.Second,
	}

	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("发送请求失败: %w", err)
	}
	defer resp.Body.Close()

	// 处理响应
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("服务器返回错误: %d - %s", resp.StatusCode, string(body))
	}

	return nil
}

// 上传文件到Nexus
func deployFile(depFiles DependencyFiles, pom Pom, config *NexusConfig, logChan chan logTask) error {
	var cmds []*exec.Cmd
	if depFiles.Jars != nil && len(depFiles.Jars) > 0 {
		for _, jar := range depFiles.Jars {
			metadata, err := extractMetadata(depFiles.Dir+"\\"+jar, pom)
			if err != nil {
				return fmt.Errorf("⚠️ 提取元数据失败: %w", err)
			}
			cmds = append(cmds, exec.Command(
				config.MvnPath,
				"-s", config.SettingPath, // 传递 settings 文件路径
				"deploy:deploy-file", // 部署目标
				"-Dmaven.wagon.http.ssl.insecure=true ",
				"-Dmaven.wagon.http.ssl.allowall=true ",
				"-Durl="+config.MavenURL,
				"-DgroupId="+pom.GroupID,
				"-DartifactId="+pom.ArtifactID,
				"-Dversion="+pom.Version,
				"-DrepositoryId=maven-releases",
				"-DgeneratePom=false",
				"-Dpackaging=jar",
				"-DpomFile="+depFiles.Pom,
				"-Dfile="+jar,
				"-Dclassifier="+metadata.Classifier,
				//"-Dsources=="+depFiles.Sources,
				//"-Djavadoc=="+depFiles.JavaDoc
			))
		}

	} else {
		cmds = append(cmds, exec.Command(
			config.MvnPath,
			"-s", config.SettingPath, // 传递 settings 文件路径
			"deploy:deploy-file", // 部署目标
			"-Dmaven.wagon.http.ssl.insecure=true ",
			"-Dmaven.wagon.http.ssl.allowall=true ",
			"-Durl="+config.MavenURL,
			"-DgroupId="+pom.GroupID,
			"-DartifactId="+pom.ArtifactID,
			"-Dversion="+pom.Version,
			"-DrepositoryId=maven-releases",
			"-DgeneratePom=false",
			"-Dpackaging=pom",
			"-DpomFile="+depFiles.Pom,
			"-Dfile="+depFiles.Pom,
		))
	}
	logTask := logTask{
		message: "",
		name:    "",
	}
	for _, cmd := range cmds {
		logTask.message = fmt.Sprintf("%s \ncd %s \n%s", logTask.message, depFiles.Dir, strings.Join(cmd.Args, " "))
		cmd.Dir = depFiles.Dir
		// 获取命令输出
		output, err := cmd.CombinedOutput()
		if err != nil {
			log.Println("cd ", depFiles.Dir)
			log.Println(strings.Join(cmd.Args, " "))
			log.Println("执行命令失败: %v\n输出: %s", err, string(output))
			logChan <- logTask
			return err
		}
		//fmt.Println("命令执行成功，输出:\n", string(output))
	}
	logTask.name = buildDependencyName(pom)
	logChan <- logTask
	return nil
}

// 构建完整文件名（包含classifier）
func buildFileName(metadata ArtifactMetadata) string {
	if metadata.Classifier == "" {
		return fmt.Sprintf("%s-%s%s", metadata.ArtifactID, metadata.Version, metadata.Extension)
	}
	return fmt.Sprintf("%s-%s-%s%s", metadata.ArtifactID, metadata.Version, metadata.Classifier, metadata.Extension)
}

// 构建依赖名
func buildDependencyName(pom Pom) string {
	return fmt.Sprintf("%s:%s:%s", pom.GroupID, pom.ArtifactID, pom.Version)
}

// 获取文件类型对应的Content-Type
func getContentType(fileName string) string {
	switch strings.ToLower(filepath.Ext(fileName)) {
	case ".pom":
		return "application/xml"
	case ".jar", ".war", ".ear":
		return "application/java-archive"
	case ".sha1", ".md5":
		return "text/plain"
	default:
		return "application/octet-stream"
	}
}
